package com.yizisu.playerlibrary.impl.exoplayer

import android.content.Context
import android.net.Uri
import android.os.Handler
import android.os.Looper
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.ext.rtmp.RtmpDataSource
import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.source.MediaSourceFactory
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.source.dash.DashMediaSource
import com.google.android.exoplayer2.source.hls.HlsMediaSource
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource
import com.google.android.exoplayer2.upstream.DataSource
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
import com.google.android.exoplayer2.upstream.cache.CacheDataSource
import com.google.android.exoplayer2.util.Util
import com.yizisu.playerlibrary.PlayerLibInit
import com.yizisu.playerlibrary.helper.PlayerModel
import com.yizisu.playerlibrary.service.download.PlayerDownload
import com.yizisu.playerlibrary.view.video_view.helper.logI
import com.yizisu.playerlibrary.view.video_view.helper.playerLibDebug
import java.net.CookieHandler
import java.net.CookieManager
import java.net.CookiePolicy


/**
 * 创建MediaSource
 */
internal fun SimpleExoPlayer.createSingleSource(
    context: Context,
    model: PlayerModel,
    exoPlayerImpl: ExoPlayerImpl<*>,
    lastPlayerError: Throwable?,
    errorListener: Function2<Throwable?, Boolean, Unit>
) {
    if (lastPlayerError == null) {
        //第一次准备播放链接回调
        if (model.cacheFirst()) {
            model.onPlayCache {
                if (it == null) {
                    model.callMediaItem { mediaItem, error, isCallOnPlayChange ->
                        dealModelCall(
                            context,
                            model,
                            exoPlayerImpl,
                            mediaItem,
                            error,
                            isCallOnPlayChange,
                            errorListener
                        )
                    }
                } else {
                    context.runOnUiThread {
                        //播放缓存成功
                        setMediaSource(it)
                        prepare()
                        errorListener.invoke(null, false)
                    }
                }
            }
        } else {
            model.callMediaItem { mediaItem, error, isCallOnPlayChange ->
                dealModelCall(
                    context,
                    model,
                    exoPlayerImpl,
                    mediaItem,
                    error,
                    isCallOnPlayChange,
                    errorListener
                )
            }
        }

    } else {
        //但播放链接出错，再次回调此函数
        model.callMediaItemWhenError(lastPlayerError) { mediaItem, error, isCallOnPlayChange ->
            dealModelCall(
                context,
                model,
                exoPlayerImpl,
                mediaItem,
                error,
                isCallOnPlayChange,
                errorListener
            )
        }
    }
}

/**
 * 处理回调结果
 */
private fun SimpleExoPlayer.dealModelCall(
    context: Context,
    model: PlayerModel,
    exoPlayerImpl: ExoPlayerImpl<*>,
    mediaItem: MediaItem?,
    error: Throwable?,
    isCallOnPlayChange: Boolean,
    errorListener: Function2<Throwable?, Boolean, Unit>
) {
    model._mediaItem = mediaItem
    if (model != exoPlayerImpl.currentPlayModel) {
        return
    }
    if (playerLibDebug) {
        logI("ExoPlay播放准备资源：url:${mediaItem?.playbackProperties?.uri}\nerror:${error?.message}\nisCallOnPlayChange:${isCallOnPlayChange}")
    }
    context.runOnUiThread {
        if (mediaItem == null) {
            if (error == null) {
                errorListener.invoke(Throwable("Uri is null"), false)
            } else {
                errorListener.invoke(error, false)
            }
        } else {
            setMediaSource(
                exoPlayerImpl.config.onBuildMediaSource(
                    mediaItem,
                    model.overrideExtension
                )
            )
            prepare()
            errorListener.invoke(null, isCallOnPlayChange)
        }
    }
}

internal val cacheDataSourceFactory by lazy {
    val cookieManager = CookieManager()
    cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER)
    CookieHandler.setDefault(cookieManager)
    CacheDataSource.Factory()
        .setCache(PlayerDownload.simpleCache)
        .setUpstreamDataSourceFactory(
            getDefaultHttpDataSourceFactory(PlayerLibInit.app)
        )
        .setCacheWriteDataSinkFactory(null)
}

class MyLoadController : DefaultLoadControl() {
    /**
     * 返回缓存时间
     */
    override fun getBackBufferDurationUs(): Long {
        return C.msToUs(30_60_1000)
    }
}


internal val mainHandler = Handler(Looper.getMainLooper())

/**
 * 是否是主线程
 */
private fun isMainThread() = Looper.getMainLooper() === Looper.myLooper()

/**
 * 代码运行在主线程
 */
private fun Context.runOnUiThread(f: Context.() -> Unit) {
    if (isMainThread()) f() else mainHandler.post { f() }
}

/**
 * 构建MediaSource，可以直接播放器播放
 */
internal fun buildMediaSource(
    mediaUri: MediaItem,
    factory: DataSource.Factory?,
    overrideExtension: String? = null
): MediaSource {
    return buildMediaSourceFactory(mediaUri, factory, overrideExtension).createMediaSource(mediaUri)
}

/**
 * 构建缓存的MediaSourceFactory
 */
internal fun buildCacheMediaSourceFactory(
    mediaUri: MediaItem,
    overrideExtension: String? = null
): MediaSourceFactory {
    return buildMediaSourceFactory(mediaUri, cacheDataSourceFactory, overrideExtension)
}

/**
 * 构建MediaSourceFactory
 */
internal fun buildMediaSourceFactory(
    mediaUri: MediaItem,
    factory: DataSource.Factory?,
    overrideExtension: String? = null
): MediaSourceFactory {
    val context: Context = PlayerLibInit.app
    val mediaFactory = factory ?: getDefaultHttpDataSourceFactory(context)
    //判断是否是rtmp流
    if (isRtmpSource(mediaUri, overrideExtension)) {
        return ProgressiveMediaSource.Factory(
            RtmpDataSource.Factory()
        )
    }
    if (isRtmpSource(mediaUri, overrideExtension)) {
        return ProgressiveMediaSource.Factory(
            RtmpDataSource.Factory()
        )
    }
    return when (val type = Util.inferContentType(
        mediaUri.playbackProperties?.uri
            ?: Uri.EMPTY, overrideExtension
    )) {
        C.TYPE_SS -> {
            SsMediaSource.Factory(
                mediaFactory
            )
        }
        C.TYPE_DASH -> {
            DashMediaSource.Factory(
                mediaFactory
            )
        }
        C.TYPE_HLS -> {
            HlsMediaSource.Factory(
                mediaFactory
            )
        }
        C.TYPE_OTHER -> {
            ProgressiveMediaSource.Factory(
                if (mediaUri.playbackProperties?.uri?.scheme == "http" || mediaUri.playbackProperties?.uri?.scheme == "https") {
                    mediaFactory
                } else {
                    factory ?: DefaultDataSourceFactory(
                        context,
                        Util.getUserAgent(context, context.packageName)
                    )
                }
            )
        }
        else -> {
            throw IllegalArgumentException("Unsupported type: $type")
        }
    }
}

internal fun getDefaultHttpDataSourceFactory(context: Context): DefaultHttpDataSource.Factory {
    return DefaultHttpDataSource.Factory().apply {
        setUserAgent(Util.getUserAgent(context, context.packageName))
        setConnectTimeoutMs(DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS)
        setReadTimeoutMs(DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS)
        setAllowCrossProtocolRedirects(true/*允许重定向*/)
    }
}

private fun isRtmpSource(uri: MediaItem, overrideExtension: String?): Boolean {
    return overrideExtension?.contains("rtmp") == true || uri.toString().startsWith("rtmp")
}

private fun isRtspSource(uri: Uri, overrideExtension: String?): Boolean {
    return overrideExtension?.contains("rtsp") == true || uri.toString().startsWith("rtsp")
}

/**
 * 毫秒换成00:00:00
 */
internal fun getCountTimeByLong(finishTime: Long): String {
    if (finishTime <= 0) {
        return "00:00"
    }
    var totalTime = (finishTime / 1000).toInt()//秒
    var hour = 0
    var minute = 0
    var second = 0

    if (3600 <= totalTime) {
        hour = totalTime / 3600
        totalTime -= 3600 * hour
    }
    if (60 <= totalTime) {
        minute = totalTime / 60
        totalTime -= 60 * minute
    }
    if (0 <= totalTime) {
        second = totalTime
    }
    val sb = StringBuilder()
    if (hour > 0) {
        if (hour < 10) {
            sb.append("0").append(hour).append(":")
        } else {
            sb.append(hour).append(":")
        }
    }
    if (minute < 10) {
        sb.append("0").append(minute).append(":")
    } else {
        sb.append(minute).append(":")
    }
    if (second < 10) {
        sb.append("0").append(second)
    } else {
        sb.append(second)
    }
    return sb.toString()
}